class QueueBad {
  private data = [];
  // 这里应该定义类型为number么？ no no 不一定，也有可能是string
  push = item => this.data.push(item);
  pop = () => this.data.shift();
}

const queueBad = new QueueBad();

queueBad.push("1.23"); // Oops，可能会有一个错误

// 一个使用者，走入了误区
console.log(queueBad.pop().toFixed(1)); // RUNTIME ERROR

// 创建一个泛型类
class Queue<T> {
  private data: T[] = [];
  // TODO: 为啥这里的函数返回值不能写T？
  public push = (item: T): T => this.data.push(item);
  public pop = (): T | undefined => this.data.shift();
}

// 简单的使用
const queue = new Queue<number>();
// queue.push(0);
// queue.push('1'); // Error：不能推入一个 `string`，只有 number 类型被允许

// 我们可以通过泛型定义输入/输出的类型
function reverse<T>(items: T[]): T[] {
  const toreturn: Array<T> = [];
  for (let i = items.length - 1; i >= 0; i--) {
    toreturn.push(items[i]);
  }
  return toreturn;
}

const sample = [1, 2, 3];
// 注意这里的reversed的类型是Array[]
const reversed = reverse(sample);

// reversed[0] = '1'; // Error
// reversed = ['1', '2']; // Error

reversed[0] = 1; // ok

// 泛型的接口
// 定义第一种接口
interface ConfigFn {
  // 第一个T带<>就可以了
  <T>(value: T): T;
}

// 写一个函数来，实现这个函数的范型接口
const fn: ConfigFn = value => value;

// 可以自行指定这个T的类型
fn<string>("haha");
fn<number>(123);

// 第二种
// 始终保持第一个T带<>
interface ConfigFn1<T> {
  (value: T): T;
}

// 实现这个函数接口
const fn1: ConfigFn1<string> = value => {
  return value;
};

fn1("haha");
// fn1(123); // error
